#include <stdint.h> /*uint32_t*/
#include <stdlib.h> /*size_t*/
#include <string.h> /*memset*/
#include "rabin_chunking.h"
#include "spooky_c.h"
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <isa-l_crypto/mh_sha1.h>


#define GSIZE32BIT 0xffffffffLL
#define SLIDE(m, fp, bufPos, buf) \
	do                            \
	{                             \
		unsigned char om;         \
		u_int64_t x;              \
		if (++bufPos >= size)     \
			bufPos = 0;           \
		om = buf[bufPos];         \
		buf[bufPos] = m;          \
		fp ^= U[om];              \
		x = fp >> shift;          \
		fp <<= 8;                 \
		fp |= m;                  \
		fp ^= T[x];               \
	} while (0)

#define SymbolCount 256
extern uint64_t g_gear_matrix[SymbolCount];

#define doRabin(hash, cbytes, kickBytes)                                                  \
	do                                                                                    \
	{                                                                                     \
		__m512i tmp_U = _mm512_i64gather_epi64(_mm512_and_epi64(kickBytes, cmask), U, 8); \
		\                                                                        
        hash = _mm512_xor_epi64(hash, tmp_U);                                             \
		__m512i tmp_x = _mm512_srli_epi64(hash, shift);                                   \
		hash = _mm512_slli_epi64(hash, 8);                                                \
		hash = _mm512_or_epi64(hash, _mm512_and_epi64(cbytes, cmask));                    \
		hash = _mm512_xor_epi64(hash, _mm512_i64gather_epi64(tmp_x, T, 8));               \
		cbytes = _mm512_srli_epi64(cbytes, 8);                                            \
		kickBytes = _mm512_srli_epi64(kickBytes, 8);                                      \
	} while (0)

#define doRabin16(hash1, hash2, cbytes, kickBytes)                                      \
	do                                                                                  \
	{                                                                                   \
		__m256i om1 = _mm512_extracti32x8_epi32(_mm512_and_epi32(kickBytes, cmask), 0); \
		__m256i om2 = _mm512_extracti32x8_epi32(_mm512_and_epi32(kickBytes, cmask), 1); \
		__m256i m1 = _mm512_extracti32x8_epi32(_mm512_and_epi32(cbytes, cmask), 0);     \
		__m256i m2 = _mm512_extracti32x8_epi32(_mm512_and_epi32(cbytes, cmask), 1);     \
		__m512i xx1 = _mm512_cvtepi32_epi64(m1);                                        \
		__m512i xx2 = _mm512_cvtepi32_epi64(m2);                                        \
		__m512i tmp_U = _mm512_i32gather_epi64(om1, U, 8);                              \
		\                                                                                 
        hash1 = _mm512_xor_epi64(hash1, tmp_U);                                         \
		\                  
        __m512i tmp_x = _mm512_srli_epi64(hash1, shift);                                \
		\        
        hash1 = _mm512_slli_epi64(hash1, 8);                                            \
		\                 
        hash1 = _mm512_or_epi64(hash1, xx1);                                            \
		hash1 = _mm512_xor_epi64(hash1, _mm512_i64gather_epi64(tmp_x, T, 8));           \
		tmp_U = _mm512_i32gather_epi64(om2, U, 8);                                      \
		\                                                                              
        hash2 = _mm512_xor_epi64(hash2, tmp_U);                                         \
		\               
        tmp_x = _mm512_srli_epi64(hash2, shift);                                        \
		\       
        hash2 = _mm512_slli_epi64(hash2, 8);                                            \
		hash2 = _mm512_or_epi64(hash2, xx2);                                            \
		\ 
        hash2 = _mm512_xor_epi64(hash2, _mm512_i64gather_epi64(tmp_x, T, 8));           \
		cbytes = _mm512_srli_epi32(cbytes, 8);                                          \
		kickBytes = _mm512_srli_epi32(kickBytes, 8);                                    \
	} while (0)

#define STRAVG 63
#define STRMIN ((STRAVG + 1) / 2)
#define STRMAX ((STRAVG + 1) * 4)

typedef unsigned int UINT32;
typedef unsigned long long int UINT64;
// time_t backup_now;

UINT64 sample_num = 0;
UINT64 unsample_num = 0;
UINT32 GEAR[256];

enum
{
	size = 48
};
UINT64 fp;
int bufpos;
UINT64 U[256];
unsigned char buf[size];
int shift;
UINT64 T[256];
UINT64 poly;

char *eFiles[] = {".pdf", ".rmv", "ra", ".bmp", ".vmem", ".vmdk", ".jpeg",
				  ".rmvb", ".exe", ".mtv", "\\Program Files", "C:\\"};

long int MiAi[256][2] =
	{
		5652377,
		45673472,
		24423617,
		146512310,
		16341615,
		142364781,
		3526234,
		1225434201,
		654410,
		324130,
		3124312,
		359840125,
		35,
		15254201,
		61,
		41870,
		212,
		3340125,
		121,
		44122,
		551,
		8431641,
		610,
		824311,
		367,
		254201,
		651,
		7632410,
		319,
		7540125,
		129,
		115422,
		551,
		34431741,
		633,
		424311,
		11,
		31,
		17,
		5,
		23,
		37,
		71,
		97,
		37,
		241,
		247,
		257,
		137,
		2391,
		431,
		831,
		2137,
		4313,
		173,
		3022467,
		523,
		4507221,
		753,
		6750467,
		143,
		412234,
		372,
		3174123,
		637,
		324123,
		821,
		25420451,
		107,
		31023,
		423,
		3512545,
		667,
		1541242,
		851,
		843541741,
		453,
		3244311,
		964,
		223401,
		910,
		3233410,
		223,
		354012523,
		827,
		12322,
		751,
		843178741,
		456,
		438911,
};

UINT32 MiAi32a[256] =
	{
		5652377,
		24423617,
		16341615,
		3526234,
		654410,
		3124312,
		35,
		61,
		212,
		121,
		551,
		610,
		367,
		651,
		319,
		129,
		551,
		633,
		11,
		17,
		23,
		71,
		37,
		247,
		137,
		431,
		2137,
		173,
		523,
		753,
		143,
		372,
		637,
		821,
		107,
		423,
		667,
		851,
		453,
		964,
		910,
		223,
		827,
		751,
		456};
UINT32 MiAi32b[256] =
	{
		45673472,
		146512310,
		142364781,
		1225434201,
		324130,
		359840125,
		15254201,
		41870,
		3340125,
		44122,
		8431641,
		824311,
		254201,
		7632410,
		7540125,
		115422,
		34431741,
		424311,
		31,
		5,
		37,
		97,
		241,
		257,
		2391,
		831,
		4313,
		3022467,
		4507221,
		6750467,
		412234,
		3174123,
		324123,
		25420451,
		31023,
		3512545,
		1541242,
		843541741,
		3244311,
		223401,
		3233410,
		354012523,
		12322,
		843178741,
		438911,
};
size_t _last_pos;
size_t _cur_pos;

unsigned int _num_chunks;

int NUMFEATURE = 4;

const char bytemsb[0x100] = {
	0,
	1,
	2,
	2,
	3,
	3,
	3,
	3,
	4,
	4,
	4,
	4,
	4,
	4,
	4,
	4,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	5,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	6,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	7,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
	8,
};

/***********************************************the rabin**********************************************/
static uint32_t fls32(UINT32 v)
{
	if (v & 0xffff0000)
	{
		if (v & 0xff000000)
			return 24 + bytemsb[v >> 24];
		else
			return 16 + bytemsb[v >> 16];
	}
	if (v & 0x0000ff00)
		return 8 + bytemsb[v >> 8];
	else
		return bytemsb[v];
}

static uint32_t fls64(UINT64 v)
{
	UINT32 h;
	if ((h = v >> 32))
		return 32 + fls32(h);
	else
		return fls32((UINT32)v);
}

UINT64 polymod(UINT64 nh, UINT64 nl, UINT64 d)
{

	int k = fls64(d) - 1;
	int i;

	// printf ("polymod : k = %d\n", k);

	d <<= 63 - k;

	// printf ("polymod : d = %llu\n", d);
	// printf ("polymod : MSB64 = %llu\n", MSB64);

	if (nh)
	{
		if (nh & MSB64)
			nh ^= d;

		// printf ("polymod : nh = %llu\n", nh);

		for (i = 62; i >= 0; i--)
			if (nh & ((UINT64)1) << i)
			{
				nh ^= d >> (63 - i);
				nl ^= d << (i + 1);

				// printf ("polymod : i = %d\n", i);
				// printf ("polymod : shift1 = %llu\n", (d >> (63 - i)));
				// printf ("polymod : shift2 = %llu\n", (d << (i + 1)));
				// printf ("polymod : nh = %llu\n", nh);
				// printf ("polymod : nl = %llu\n", nl);
			}
	}
	for (i = 63; i >= k; i--)
	{
		if (nl & (long long int)(1) << i)
			nl ^= d >> 63 - i;

		// printf ("polymod : nl = %llu\n", nl);
	}

	// printf ("polymod : returning %llu\n", nl);

	return nl;
}

void polymult(UINT64 *php, UINT64 *plp, UINT64 x, UINT64 y)
{

	int i;

	// printf ("polymult (x %llu y %llu)\n", x, y);

	UINT64 ph = 0, pl = 0;
	if (x & 1)
		pl = y;
	for (i = 1; i < 64; i++)
		if (x & ((long long int)(1) << i))
		{

			// printf ("polymult : i = %d\n", i);
			// printf ("polymult : ph = %llu\n", ph);
			// printf ("polymult : pl = %llu\n", pl);
			// printf ("polymult : y = %llu\n", y);
			// printf ("polymult : ph ^ y >> (64-i) = %llu\n", (ph ^ y >> (64-i)));
			// printf ("polymult : pl ^ y << i = %llu\n", (pl ^ y << i));

			ph ^= y >> (64 - i);
			pl ^= y << i;

			// printf ("polymult : ph %llu pl %llu\n", ph, pl);
		}
	if (php)
		*php = ph;
	if (plp)
		*plp = pl;

	// printf ("polymult : h %llu l %llu\n", ph, pl);
}

UINT64 append8(UINT64 p, unsigned char m)
{
	return ((p << 8) | m) ^ T[p >> shift];
}

UINT64 slide8(unsigned char m)
{
	unsigned char om;
	// printf("this char is %c\n",m);
	if (++bufpos >= size)
		bufpos = 0;
	om = buf[bufpos];
	buf[bufpos] = m;
	return fp = append8(fp ^ U[om], m);
}

UINT64 polymmult(UINT64 x, UINT64 y, UINT64 d)
{

	// printf ("polymmult (x %llu y %llu d %llu)\n", x, y, d);

	UINT64 h, l;
	polymult(&h, &l, x, y);
	return polymod(h, l, d);
}

void calcT(UINT64 poly)
{

	int j;
	UINT64 T1;

	// printf ("rabinpoly::calcT ()\n");

	int xshift = fls64(poly) - 1;
	shift = xshift - 8;
	T1 = polymod(0, (long long int)(1) << xshift, poly);
	for (j = 0; j < 256; j++)
	{
		T[j] = polymmult(j, T1, poly) | ((UINT64)j << xshift);

		// printf ("rabinpoly::calcT tmp = %llu\n", polymmult (j, T1, poly));
		// printf ("rabinpoly::calcT shift = %llu\n", ((UINT64) j << xshift));
		// printf ("rabinpoly::calcT xshift = %d\n", xshift);
		// printf ("rabinpoly::calcT T[%d] = %llu\n", j, T[j]);
	}

	// printf ("rabinpoly::calcT xshift = %d\n", xshift);
	// printf ("rabinpoly::calcT T1 = %llu\n", T1);
	// printf ("rabinpoly::calcT T = {");
	// for (i=0; i< 256; i++)
	// printf ("\t%llu \n", T[i]);
	// printf ("}\n");
}

void rabinpoly_init(UINT64 p)
{
	poly = p;
	calcT(poly);
}

void window_init(UINT64 poly)
{

	int i;
	UINT64 sizeshift;

	rabinpoly_init(poly);
	fp = 0;
	bufpos = -1;
	sizeshift = 1;
	for (i = 1; i < size; i++)
		sizeshift = append8(sizeshift, 0);
	for (i = 0; i < 256; i++)
		U[i] = polymmult(i, sizeshift, poly);
	memset((char *)buf, 0, sizeof(buf));
}

void windows_reset()
{
	fp = 0;
	// memset((char*) buf,0,sizeof (buf));
	// memset((char*) chunk,0,sizeof (chunk));
}

void chunkAlg_init()
{
	window_init(FINGERPRINT_PT);
	_last_pos = 0;
	_cur_pos = 0;
	windows_reset();
	_num_chunks = 0;
}

void gear_init()
{
	char seed[32];
	int i, j;
	for (i = 0; i < 256; i++)
	{
		for (j = 0; j < 32; j++)
		{
			seed[j] = i;
		}
		GEAR[i] = 0;
		{
			unsigned char digest[16];
			MD5_CTX ctx;
			MD5_Init(&ctx);
			MD5_Update(&ctx, seed, 32);
			MD5_Final(digest, &ctx);
			memcpy(&GEAR[i], digest, 7);
		}
	}
}

int rabin_chunk_data(unsigned char *p, int n)
{

	UINT64 f_break = 0;
	UINT64 count = 0;
	UINT64 fp = 0;
	int i = 1, bufPos = -1;

	unsigned char om;
	u_int64_t x;

	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	if (n <= destor.chunk_min_size)
		return n;
	else
		i = destor.chunk_min_size;
	while (i <= n)
	{

		SLIDE(p[i - 1], fp, bufPos, buf);
		if (destor.chunk_algorithm == CHUNK_RABIN)
		{
			if (((fp & (destor.chunk_avg_size - 1)) == BREAKMARK_VALUE && i >= destor.chunk_min_size) || i >= destor.chunk_max_size || i == n)
			{
				break;
			}
			else
				i++;
		}
		else if (destor.chunk_algorithm == CHUNK_NORMALIZED_RABIN)
		{
			if (i < destor.chunk_avg_size)
			{
				if (((fp & (destor.chunk_avg_size * 2 - 1)) == BREAKMARK_VALUE && i >= destor.chunk_min_size) || i >= destor.chunk_max_size || i == n)
				{
					break;
				}
				else
					i++;
			}
			else
			{
				if (((fp & (destor.chunk_avg_size / 2 - 1)) == BREAKMARK_VALUE && i >= destor.chunk_min_size) || i >= destor.chunk_max_size || i == n)
				{
					break;
				}
				else
					i++;
			}
		}
	}
	return i;
}

const unsigned int RABIN_WINDOW_AVX8_SIZE = sizeof(struct RabinWindowAvx8);

void know_rabin_window_size() {
	printf("%d\n",RABIN_WINDOW_AVX8_SIZE);
}

void rabin_window_avx8_init(struct RabinWindowAvx8* w,unsigned char *p, unsigned int n) {
	assert(n >= 0x10000);
	w->p = p;
	w->i = 0;
	w->j = 8;
	unsigned int subregion_size = (0x10000-48)/8;
	w->subregion_size = subregion_size;
	w->vindex = _mm512_setr_epi64(
		0, 1 * subregion_size, 2 * subregion_size , 3 * subregion_size,
		4 * subregion_size, 5 * subregion_size , 6 * subregion_size, 7 * subregion_size);
	w->cmask = _mm512_set1_epi64((long)0xff);

	w->dmask = _mm512_set1_epi64((long)destor.chunk_avg_size);
	w->break_mark = _mm512_set1_epi64((long)BREAKMARK_VALUE);

	w->hash = _mm512_set1_epi64(0LL);

	w->cbytes = _mm512_set1_epi64(0LL);
	w->kickBytes = _mm512_set1_epi64(0LL);
}

uint rabin_window_cursor(struct RabinWindowAvx8* w) {
	return w->i + w->j + 1;
}

unsigned char rabin_chunk_data_avx8(struct RabinWindowAvx8* w)
{
	__m512i cmask = w->cmask;
	while (w->i < w->subregion_size + 48)
	{
		if(w->j == 8) {
			w->j = 0;
			w->cbytes = _mm512_i64gather_epi64(w->vindex, (void *)(((uint8_t *)(w->p)) + w->i), 1);
			if (w->i >= 48)
			{
				w->kickBytes = _mm512_i64gather_epi64(w->vindex, (void *)(((uint8_t *)(w->p)) + w->i - 48), 1);
			}
		}
		while(w->j < 8)
		{
			doRabin((w->hash), (w->cbytes), (w->kickBytes));
			(w->j)++;
			__m512i tmp_feature = _mm512_and_epi64((w->hash), (w->dmask));
			__mmask8 result =_mm512_cmpeq_epu64_mask(tmp_feature,(w->break_mark));
			if(result != 0) {
				if(w->j == 8) {
					w->i += 8;
				}
				return result;
			}
		}
		w->i += 8;
	}
	return 0;
}


void odess_sisd(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	UINT64 SFeature = 0;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	i = 0;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	for (subregion_num = 0, jj = 0; subregion_num < destor.super_feature_subregion_num;
		 subregion_num++, jj++)
	{
		endpos += subregion_size;
		if (endpos > n)
			endpos = n;

		while (i <= endpos)
		{
			fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);

			if (Feature[jj] < fingerprint)
				Feature[jj] = fingerprint;

			i++;
		}
	}

	UINT64 TempFeatures[12];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 0);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);

	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);

	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
}

/*
 * It isn't suitable for AVX because of the hash judgement
 */
void odess(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	UINT64 SFeature = 0;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	i = 0;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	while (i <= n)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
		if ((!(fingerprint & 0x000088035100)))
		{
			for (jj = 0; jj < 12; jj++)
			{
				SFeature = (MiAi[jj][0] * fingerprint + MiAi[jj][1]) & GSIZE32BIT; //&GSIZE32BIT;
				if (SFeature > Feature[jj])
				{
					Feature[jj] = SFeature;
				}
			}
		}
		i++;
	}

	UINT64 TempFeatures[12];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 0);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);

	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);

	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
}

int gear_roll_hash(unsigned char *p, int n, struct superF *SF) {
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	int odess_sample_number = 0;
	UINT64 fingerprint;
	UINT64 SFeature = 0;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	i = 0;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	while (i <= n)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
		if ((!(fingerprint & 0x000088035100)))
		{
			odess_sample_number ++;
		}
		i++;
	}
	return odess_sample_number;
}

void test_pack_feature(struct superF *SF) {
	UINT64 Feature[24];
	UINT64 TempFeatures[12];
	int feature_size = 4 * sizeof(UINT64);
	memset((char *)Feature, 0, sizeof(Feature));

	TempFeatures[0] = *(Feature + 0);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);

	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);

	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
}

/*
 * In smooth it can be suitable for AVX.
 */
void odess_smooth(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 fingerprint;
	UINT64 SFeature = 0;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	i = 0;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	__m512i multiplication = _mm512_set_epi32(MiAi[15][0], MiAi[14][0], MiAi[13][0], MiAi[12][0], 
												MiAi[11][0], MiAi[10][0], MiAi[9][0], MiAi[8][0],
												MiAi[7][0], MiAi[6][0], MiAi[5][0], MiAi[4][0], 
												MiAi[3][0], MiAi[2][0], MiAi[1][0], MiAi[0][0]);
	__m512i addition = _mm512_set_epi32(MiAi[15][1], MiAi[14][1], MiAi[13][1], MiAi[12][1], 
												MiAi[11][1], MiAi[10][1], MiAi[9][1], MiAi[8][1],
												MiAi[7][1], MiAi[6][1], MiAi[5][1], MiAi[4][1], 
												MiAi[3][1], MiAi[2][1], MiAi[1][1], MiAi[0][1]);
	__m512i Feature =  _mm512_set1_epi32(0);

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	while (i <= n)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
		if ((!(fingerprint & 0x000088035100)))
		{
			__m512i fp_vec = _mm512_set1_epi32((long)fingerprint);
			__m512i immediate = _mm512_mullo_epi32(fp_vec,multiplication);
			__m512i SFeature = _mm512_add_epi32(_mm512_mullo_epi32(fp_vec,multiplication),addition);
			Feature = _mm512_max_epu32(SFeature,Feature);
		}
		i++;
	}

	
	uint32_t TempFeatures[16] __attribute__((aligned (64)));
	_mm512_store_epi32(TempFeatures,Feature);

	int feature_size = 4 * sizeof(uint32_t);
	uint64_t temp_hash = spooky64_hash32((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 4), feature_size, 12345678);
	SF->sf2 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 8), feature_size, 12345678);
	SF->sf3 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 12), feature_size, 12345678);
	SF->sf4 = temp_hash;
}


void super_feature(unsigned char *p, int n, struct superF *SF)
{
	int number = 3;
	UINT64 SFeature, Feature[124];
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	for (j = 48; j >= 2; j--)
	{
		SLIDE(p[i - j], fingerprint, bufPos, buf);
	}

	while (i <= n)
	{
		SLIDE(p[i - 1], fingerprint, bufPos, buf);
		for (jj = 0; jj < number * NUMFEATURE; jj++)
		{
			SFeature = (MiAi[jj][0] * fingerprint + MiAi[jj][1]) & GSIZE32BIT; //&GSIZE32BIT;
			if (SFeature > Feature[jj])
			{
				Feature[jj] = SFeature;
			}
		}
		i++;
	}

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	//	printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

void super_feature_smooth(unsigned char *p, int n, struct superF *SF)
{
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	__m512i multiplication = _mm512_set_epi32(MiAi[15][0], MiAi[14][0], MiAi[13][0], MiAi[12][0], 
												MiAi[11][0], MiAi[10][0], MiAi[9][0], MiAi[8][0],
												MiAi[7][0], MiAi[6][0], MiAi[5][0], MiAi[4][0], 
												MiAi[3][0], MiAi[2][0], MiAi[1][0], MiAi[0][0]);
	__m512i addition = _mm512_set_epi32(MiAi[15][1], MiAi[14][1], MiAi[13][1], MiAi[12][1], 
												MiAi[11][1], MiAi[10][1], MiAi[9][1], MiAi[8][1],
												MiAi[7][1], MiAi[6][1], MiAi[5][1], MiAi[4][1], 
												MiAi[3][1], MiAi[2][1], MiAi[1][1], MiAi[0][1]);
	__m512i Feature =  _mm512_set1_epi32(0);

	for (j = 48; j >= 2; j--)
	{
		SLIDE(p[i - j], fingerprint, bufPos, buf);
	}

	while (i <= n)
	{
		SLIDE(p[i - 1], fingerprint, bufPos, buf);
		__m512i fp_vec = _mm512_set1_epi32((long)fingerprint);
		__m512i immediate = _mm512_mullo_epi32(fp_vec,multiplication);
		__m512i SFeature = _mm512_add_epi32(_mm512_mullo_epi32(fp_vec,multiplication),addition);
		Feature = _mm512_max_epu32(SFeature,Feature);
		i++;
	}

	uint32_t TempFeatures[16] __attribute__((aligned (64)));
	_mm512_store_epi32(TempFeatures,Feature);

	int feature_size = 4 * sizeof(uint32_t);
	uint64_t temp_hash = spooky64_hash32((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 4), feature_size, 12345678);
	SF->sf2 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 8), feature_size, 12345678);
	SF->sf3 = temp_hash;

	temp_hash = spooky64_hash32((unsigned char *)(TempFeatures + 12), feature_size, 12345678);
	SF->sf4 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	//	printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

/*
 * 16-way parallel Rabin rolling hash algorithm
 * Because the chunk is divided into 16 subchunks,
 * the sfs are a little different from the sfs in super_feature
 */
void super_feature_avx(unsigned char *p, int n, struct superF *SF)
{
	int number = 3;
	UINT64 SFeature, Feature[24];
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	int subregion_size = n / 16;
	if (subregion_size < 48)
		assert(subregion_size > 48);

	int addition = 48;
	if ((subregion_size + addition) % 4 != 0)
	{
		addition = 4 - (subregion_size + addition) % 4 + addition;
		assert((addition + subregion_size) % 4 == 0);
	}

	__m512i vindex = _mm512_setr_epi32(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition,
		8 * subregion_size - addition, 9 * subregion_size - addition, 10 * subregion_size - addition, 11 * subregion_size - addition,
		12 * subregion_size - addition, 13 * subregion_size - addition, 14 * subregion_size - addition, 15 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi32((long)0xff);
	__m512i submask = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, 0xffffffffffffffff);
	__m512i gsize = _mm512_set1_epi64((long long)0xffffffff);

	i = 0;
	__m512i hash1 = _mm512_set1_epi64(0LL);
	__m512i hash2 = _mm512_set1_epi64(0LL);
	__m512i kickBytes = _mm512_set1_epi32(0LL);
	__m512i sfeature = _mm512_set1_epi64(0LL);
	__m512i sfeature1 = _mm512_set1_epi64(0LL);
	__m512i sfeature2 = _mm512_set1_epi64(0LL);
	while (i < subregion_size + addition)
	{
		__m512i cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}

		for (j = 0; j < 4; j++)
		{
			// assert(i + j + 11 * subregion_size - addition < n);
			doRabin16(hash1, hash2, cbytes, kickBytes);
			if (i + j >= 47 && i + j < addition)
			{
				for (jj = 0; jj < number * NUMFEATURE; jj++)
				{
					sfeature = _mm512_and_epi64(MiAi[jj][0] * hash1 + MiAi[jj][1], gsize);
					sfeature = _mm512_and_epi64(sfeature, submask);
					Feature[jj] = max(Feature[jj], _mm512_reduce_max_epi64(sfeature));
				}
			}
			else if (i + j >= addition)
			{
				for (jj = 0; jj < number * NUMFEATURE; jj++)
				{
					sfeature1 = _mm512_and_epi64(MiAi[jj][0] * hash1 + MiAi[jj][1], gsize);
					sfeature2 = _mm512_and_epi64(MiAi[jj][0] * hash2 + MiAi[jj][1], gsize);
					sfeature = _mm512_max_epi64(sfeature1, sfeature2);
					Feature[jj] = max(Feature[jj], _mm512_reduce_max_epi64(sfeature));
				}
			}
		}

		i += 4;
	}

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

// faster than super_feature_avx
void super_feature_avx_fast(unsigned char *p, int n, struct superF *SF)
{
	int number = 3;
	UINT64 SFeature;
	UINT32 Feature[24];
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	int subregion_size = n / 16;
	if (subregion_size < 48)
		assert(subregion_size > 48);

	int addition = 48;
	if ((subregion_size + addition) % 4 != 0)
	{
		addition = 4 - (subregion_size + addition) % 4 + addition;
		assert((addition + subregion_size) % 4 == 0);
	}

	__m512i vindex = _mm512_setr_epi32(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition,
		8 * subregion_size - addition, 9 * subregion_size - addition, 10 * subregion_size - addition, 11 * subregion_size - addition,
		12 * subregion_size - addition, 13 * subregion_size - addition, 14 * subregion_size - addition, 15 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi32((long)0xff);

	i = 0;
	__m512i hash1 = _mm512_setzero_epi32();
	__m512i hash2 = _mm512_setzero_epi32();
	__m512i hash;
	__m512i cbytes;
	__m512i kickBytes;
	__m512i vfeature0 = _mm512_setzero_epi32();
	__m512i vfeature1 = _mm512_setzero_epi32();
	__m512i vfeature2 = _mm512_setzero_epi32();
	__m512i vfeature3 = _mm512_setzero_epi32();
	__m512i vfeature4 = _mm512_setzero_epi32();
	__m512i vfeature5 = _mm512_setzero_epi32();
	__m512i vfeature6 = _mm512_setzero_epi32();
	__m512i vfeature7 = _mm512_setzero_epi32();
	__m512i vfeature8 = _mm512_setzero_epi32();
	__m512i vfeature9 = _mm512_setzero_epi32();
	__m512i vfeature10 = _mm512_setzero_epi32();
	__m512i vfeature11 = _mm512_setzero_epi32();

	__m256i hash1_32, hash2_32;

	while (i < subregion_size + addition)
	{
		cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}

		for (j = 0; j < 4; j++)
		{
			// assert(i + j + 11 * subregion_size - addition < n);
			doRabin16(hash1, hash2, cbytes, kickBytes);
			if (i + j >= 47 && i + j < addition)
			{
				hash1_32 = _mm512_cvtepi64_epi32(hash1);
				UINT32 fp1 = _mm256_extract_epi32(hash1_32, 0);
				for (jj = 0; jj < 12; jj++)
				{
					SFeature = (MiAi32a[jj] * fp1 + MiAi32b[jj]) & GSIZE32BIT;
					if (SFeature > Feature[jj])
					{
						Feature[jj] = SFeature;
					}
				}
			}
			else if (i + j >= addition)
			{
				hash1_32 = _mm512_cvtepi64_epi32(hash1);
				hash2_32 = _mm512_cvtepi64_epi32(hash2);

				hash = _mm512_inserti64x4(hash, hash1_32, 0);
				hash = _mm512_inserti64x4(hash, hash2_32, 1);

				__m512i firsta1 = _mm512_set1_epi32(MiAi32a[0]);
				__m512i firstb1 = _mm512_set1_epi32(MiAi32b[0]);
				__m512i phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature0 = _mm512_max_epu32(vfeature0, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[1]);
				firstb1 = _mm512_set1_epi32(MiAi32b[1]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature1 = _mm512_max_epu32(vfeature1, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[2]);
				firstb1 = _mm512_set1_epi32(MiAi32b[2]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature2 = _mm512_max_epu32(vfeature2, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[3]);
				firstb1 = _mm512_set1_epi32(MiAi32b[3]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature3 = _mm512_max_epu32(vfeature3, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[4]);
				firstb1 = _mm512_set1_epi32(MiAi32b[4]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature4 = _mm512_max_epu32(vfeature4, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[5]);
				firstb1 = _mm512_set1_epi32(MiAi32b[5]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature5 = _mm512_max_epu32(vfeature5, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[6]);
				firstb1 = _mm512_set1_epi32(MiAi32b[6]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature6 = _mm512_max_epu32(vfeature6, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[7]);
				firstb1 = _mm512_set1_epi32(MiAi32b[7]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature7 = _mm512_max_epu32(vfeature7, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[8]);
				firstb1 = _mm512_set1_epi32(MiAi32b[8]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature8 = _mm512_max_epu32(vfeature8, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[9]);
				firstb1 = _mm512_set1_epi32(MiAi32b[9]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature9 = _mm512_max_epu32(vfeature9, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[10]);
				firstb1 = _mm512_set1_epi32(MiAi32b[10]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature10 = _mm512_max_epu32(vfeature10, phash);

				firsta1 = _mm512_set1_epi32(MiAi32a[11]);
				firstb1 = _mm512_set1_epi32(MiAi32b[11]);
				phash = _mm512_add_epi32(_mm512_mullo_epi32(hash, firsta1), firstb1);
				vfeature11 = _mm512_max_epu32(vfeature11, phash);
			}
		}

		i += 4;
	}

	Feature[0] = max(Feature[0], _mm512_reduce_max_epu32(vfeature0));
	Feature[1] = max(Feature[1], _mm512_reduce_max_epu32(vfeature1));
	Feature[2] = max(Feature[2], _mm512_reduce_max_epu32(vfeature2));
	Feature[3] = max(Feature[3], _mm512_reduce_max_epu32(vfeature3));
	Feature[4] = max(Feature[4], _mm512_reduce_max_epu32(vfeature4));
	Feature[5] = max(Feature[5], _mm512_reduce_max_epu32(vfeature5));
	Feature[6] = max(Feature[6], _mm512_reduce_max_epu32(vfeature6));
	Feature[7] = max(Feature[7], _mm512_reduce_max_epu32(vfeature7));
	Feature[8] = max(Feature[8], _mm512_reduce_max_epu32(vfeature8));
	Feature[9] = max(Feature[9], _mm512_reduce_max_epu32(vfeature9));
	Feature[10] = max(Feature[10], _mm512_reduce_max_epu32(vfeature10));
	Feature[11] = max(Feature[11], _mm512_reduce_max_epu32(vfeature11));

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature);
	TempFeatures[1] = *(Feature + 1);
	TempFeatures[2] = *(Feature + 2);
	TempFeatures[3] = *(Feature + 3);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 4);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

// faster than Finesse_avx
void sisd_rabin_fixed_avx(unsigned char *p, int n, struct superF *SF)
{
	destor.super_feature_subregion_num = 16;
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	__attribute__((aligned(64))) unsigned char buf[12][64];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	if (subregion_size < 48)
		assert(subregion_size > 48);

	int addition = 48;
	if ((subregion_size + addition) % 4 != 0)
	{
		addition = 4 - (subregion_size + addition) % 4 + addition;
		assert((addition + subregion_size) % 4 == 0);
	}

	__m512i vindex = _mm512_setr_epi32(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition,
		8 * subregion_size - addition, 9 * subregion_size - addition, 10 * subregion_size - addition, 11 * subregion_size - addition,
		12 * subregion_size - addition, 13 * subregion_size - addition, 14 * subregion_size - addition, 15 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi32((long)0xff);
	__m512i submask = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, 0xffffffffffffffff);
	__m512i vfeature1 = _mm512_set1_epi64(0LL);
	__m512i vfeature2 = _mm512_set1_epi64(0LL);

	i = 0;
	__m512i hash1 = _mm512_set1_epi64(0LL);
	__m512i hash2 = _mm512_set1_epi64(0LL);

	int64_t tmphash = 0;

	__m512i cbytes = _mm512_set1_epi32(0LL);
	__m512i kickBytes = _mm512_set1_epi32(0LL);

	while (i < subregion_size + addition)
	{
		cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}

		for (j = 0; j < 4; j++)
		{
			doRabin16(hash1, hash2, cbytes, kickBytes);
			if (i + j >= 47 && i + j < addition)
			{
				vfeature1 = _mm512_max_epi64(vfeature1, hash1);
				if (i + j == addition - 1)
				{
					vfeature1 = _mm512_and_epi64(vfeature1, submask);
				}
			}
			else if (i + j >= addition)
			{
				vfeature1 = _mm512_max_epi64(vfeature1, hash1);
				vfeature2 = _mm512_max_epi64(vfeature2, hash2);
				if (i + j == subregion_size - 1)
				{
					__m512i vfeature3 = _mm512_and_epi64(vfeature1, submask);
					tmphash = _mm512_reduce_or_epi64(vfeature3);
				}
			}
		}
		i += 4;
	}

	_mm512_store_epi64(Feature, vfeature1);
	_mm512_store_epi64(Feature + 8, vfeature2);

	Feature[0] = tmphash;

	Feature[0] = max(Feature[0], Feature[1]);
	Feature[1] = max(Feature[2], Feature[3]);
	Feature[2] = max(Feature[4], Feature[5]);
	Feature[3] = max(Feature[6], Feature[7]);
	Feature[4] = max(Feature[8], Feature[9]);
	Feature[5] = max(Feature[10], Feature[11]);
	Feature[6] = max(Feature[12], Feature[13]);
	Feature[7] = max(Feature[14], Feature[15]);

	Feature[8] = max(Feature[0], Feature[1]);
	Feature[9] = max(Feature[2], Feature[3]);
	Feature[10] = max(Feature[4], Feature[5]);
	Feature[11] = max(Feature[6], Feature[7]);

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 0);
	TempFeatures[1] = *(Feature + 2);
	TempFeatures[2] = *(Feature + 4);
	TempFeatures[3] = *(Feature + 6);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 1);
	TempFeatures[1] = *(Feature + 3);
	TempFeatures[2] = *(Feature + 5);
	TempFeatures[3] = *(Feature + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;

	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

void Finesse_avx(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	if (subregion_size < 48)
		assert(subregion_size > 48);
	__m512i vindex = _mm512_setr_epi32(0, 1 * subregion_size, 2 * subregion_size, 3 * subregion_size,
									   4 * subregion_size, 5 * subregion_size, 6 * subregion_size, 7 * subregion_size,
									   8 * subregion_size, 9 * subregion_size, 10 * subregion_size, 11 * subregion_size,
									   12 * subregion_size, 13 * subregion_size, 14 * subregion_size, 14 * subregion_size);
	if (destor.super_feature_subregion_num == 12)
		vindex = _mm512_setr_epi32(0, 1 * subregion_size, 2 * subregion_size, 3 * subregion_size,
								   4 * subregion_size, 5 * subregion_size, 6 * subregion_size, 7 * subregion_size,
								   8 * subregion_size, 9 * subregion_size, 10 * subregion_size, 11 * subregion_size,
								   8 * subregion_size, 9 * subregion_size, 10 * subregion_size, 11 * subregion_size);
	__m512i cmask = _mm512_set1_epi32((long)0xff);
	__m512i vfeature1 = _mm512_set1_epi64(0LL);
	__m512i vfeature2 = _mm512_set1_epi64(0LL);

	i = 0;
	j = 0;
	__m512i hash1 = _mm512_set1_epi64(0LL);
	__m512i hash2 = _mm512_set1_epi64(0LL);
	__m512i kickBytes = _mm512_set1_epi32(0LL);
	while (i < subregion_size)
	{
		__m512i cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}

		for (j = 0; j < 4; j++)
		{
			doRabin16(hash1, hash2, cbytes, kickBytes);
			if (i >= 48)
			{
				vfeature1 = _mm512_max_epi64(vfeature1, hash1);
				vfeature2 = _mm512_max_epi64(vfeature2, hash2);
			}
		}

		i += 4;
		if (i + 4 >= subregion_size)
			break;
	}

	// __m512i vindex2 = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7);
	// _mm512_i64scatter_epi64((void *)Feature, vindex2, vfeature1, 8);
	// vindex2 = _mm512_setr_epi64(8, 9, 10, 11, 12, 13, 14, 14);
	// if (destor.super_feature_subregion_num == 12)
	// 	vindex2 = _mm512_setr_epi64(8, 9, 10, 11, 8, 9, 10, 11);
	// _mm512_i64scatter_epi64((void *)Feature, vindex2, vfeature2, 8);

	_mm512_store_epi64(Feature, vfeature1);
	_mm512_store_epi64(Feature + 8, vfeature2);

	/* grouping phase */
	if (destor.features_per_sf == 3)
		get_sketch_threefeature_per_sf(SF, Feature);
	else if (destor.features_per_sf == 4)
	{
		if (destor.enable_sort_features)
			get_sketch_fourfeature_per_sf_sorted(SF, Feature);
		else
			get_sketch_threefeature_four_sf(SF, Feature);
	}
	else
	{
		get_sketch_fivefeature_per_sf(SF, Feature);
	}

	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

UINT64 rabin_only(unsigned char *p, int n)
{
	int number = 3;
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	for (j = 48; j >= 2; j--)
	{
		SLIDE(p[i - j], fingerprint, bufPos, buf);
	}

	while (i <= n)
	{
		SLIDE(p[i - 1], fingerprint, bufPos, buf);
		i++;
	}
	return fingerprint;
}

UINT64 rabin_only_avx8(unsigned char *p, int n)
{
	destor.super_feature_subregion_num = 8;
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 fingerprint;
	UINT64 Feature[24];
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, sizeof(buf));
	memset((char *)Feature, 0, sizeof(Feature));

	if (subregion_size < 48)
		assert(subregion_size > 48);

	int addition = 48;
	if ((subregion_size + addition) % 8 != 0)
	{
		addition = 8 - (subregion_size + addition) % 8 + addition;
		assert((addition + subregion_size) % 8 == 0);
	}

	__m512i vindex = _mm512_setr_epi64(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi64((long)0xff);
	__m512i submask = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, 0xffffffffffffffff);
	__m512i vfeature = _mm512_set1_epi64(0LL);

	i = 0;
	__m512i hash = _mm512_set1_epi64(0LL);

	int64_t tmphash = 0;

	__m512i cbytes = _mm512_set1_epi64(0LL);
	__m512i kickBytes = _mm512_set1_epi64(0LL);

	while (i < subregion_size + addition)
	{
		cbytes = _mm512_i64gather_epi64(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i64gather_epi64(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}
		// _mm512_store_epi64(buf[(i / 8) % 6], cbytes);
		for (j = 0; j < 8; j++)
		{
			// assert(i + j + 11 * subregion_size - addition < n);
			doRabin(hash, cbytes, kickBytes);
		}
		i += 8;
	}

	_mm512_store_epi64(Feature, hash);
	return Feature[0];
}

UINT64 rabin_only_avx16(unsigned char *p, int n)
{
	destor.super_feature_subregion_num = 16;
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	UINT64 Feature[16];
	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	if (subregion_size < 48)
		return 0;

	int addition = 48;
	if ((subregion_size + addition) % 4 != 0)
	{
		addition = 4 - (subregion_size + addition) % 4 + addition;
		assert((addition + subregion_size) % 4 == 0);
	}

	__m512i vindex = _mm512_setr_epi32(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition,
		8 * subregion_size - addition, 9 * subregion_size - addition, 10 * subregion_size - addition, 11 * subregion_size - addition,
		12 * subregion_size - addition, 13 * subregion_size - addition, 14 * subregion_size - addition, 15 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi32((long)0xff);
	__m512i submask = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, 0xffffffffffffffff);
	__m512i vfeature1 = _mm512_set1_epi64(0LL);
	__m512i vfeature2 = _mm512_set1_epi64(0LL);

	i = 0;
	__m512i hash1 = _mm512_set1_epi64(0LL);
	__m512i hash2 = _mm512_set1_epi64(0LL);

	int64_t tmphash = 0;

	__m512i cbytes = _mm512_set1_epi32(0LL);
	__m512i kickBytes = _mm512_set1_epi32(0LL);

	while (i < subregion_size + addition)
	{
		cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		if (i >= 48)
		{
			kickBytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i - 48), 1);
		}
		for (j = 0; j < 4; j++)
		{
			doRabin16(hash1, hash2, cbytes, kickBytes);
		}
		i += 4;
	}

	_mm512_store_epi64(Feature, hash1);
	_mm512_store_epi64(Feature + 8, hash2);
	return Feature[0];
}

void finesse_only(unsigned char *p, int n, UINT64 *Feature)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);

	for (j = 48; j >= 2; j--)
	{
		SLIDE(p[i - j], fingerprint, bufPos, buf);
	}

	int endpos = 0;
	for (subregion_num = 0, jj = 0; subregion_num < destor.super_feature_subregion_num;
		 subregion_num++, jj++)
	{
		endpos += subregion_size;
		if (endpos > n)
			endpos = n;

		while (i <= endpos)
		{
			SLIDE(p[i - 1], fingerprint, bufPos, buf);
			if (fingerprint > Feature[jj])
				Feature[jj] = fingerprint;
			i++;
		}
	}
	return;
}

void gear_only(unsigned char *p, int n, UINT64 *Feature)
{
	int number = 3;
	UINT64 fingerprint = 0;
	int i = 48, j, bufPos = -1, jj, ii;

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	while (i <= n)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
		i++;
	}

	Feature[0] = fingerprint;
	Feature[1] = fingerprint;
	Feature[2] = fingerprint;
	Feature[3] = fingerprint;
	Feature[4] = fingerprint;
	Feature[5] = fingerprint;
	Feature[6] = fingerprint;
	Feature[7] = fingerprint;
	Feature[8] = fingerprint;
	Feature[9] = fingerprint;
	Feature[10] = fingerprint;
	Feature[11] = fingerprint;
	return;
}

void odess_only(unsigned char *p, int n, UINT64 *Feature)
{
	int number = 3;
	UINT64 fingerprint = 0, SFeature;
	int i = 48, j, bufPos = -1, jj, ii;

	for (i = 1; i <= 31; i++)
	{
		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);
	}

	while (i <= n)
	{

		fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i - 1]]);

		if ((!(fingerprint & 0x000088035100)))
		{
			for (jj = 0; jj < 12; jj++)
			{
				SFeature = (MiAi[jj][0] * fingerprint + MiAi[jj][1]) & GSIZE32BIT; //&GSIZE32BIT;
				if (SFeature > Feature[jj])
				{
					Feature[jj] = SFeature;
				}
			}
		}
		i++;
	}
	return;
}

void Finesse(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	i = 48;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (j = 48; j >= 2; j--)
		SLIDE(p[i - j], fingerprint, bufPos, buf);

	for (subregion_num = 0, jj = 0; subregion_num < destor.super_feature_subregion_num;
		 subregion_num++, jj++)
	{
		endpos += subregion_size;
		if (endpos > n)
			endpos = n;

		while (i <= endpos)
		{
			SLIDE(p[i - 1], fingerprint, bufPos, buf);
			if (fingerprint > Feature[jj])
				Feature[jj] = fingerprint;
			i++;
		}
	}
	/* grouping phase */
	if (destor.features_per_sf == 3)
		get_sketch_threefeature_per_sf(SF, Feature);
	else if (destor.features_per_sf == 4)
	{
		if (destor.enable_sort_features)
			get_sketch_fourfeature_per_sf_sorted(SF, Feature);
		else
			get_sketch_threefeature_four_sf(SF, Feature);
	}
	else
	{
		get_sketch_fivefeature_per_sf(SF, Feature);
	}

	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

void zok(unsigned char *p, int n, struct superF *SF) {
	int step = (1 << 8) / 12;
	int addtion = 0;
	int i = 0;
	int sub_region = 0;
	int mapping = 0;

	int bufPos[12];
	int feature_size;
	UINT64 fingerprint[12];
	UINT64 Feature[12];
	UINT64 TempFeatures[4];
	unsigned char buf[12][64];
	unsigned char max_e[12];
	memset((char *)buf, 0, sizeof(buf));
	memset((char *)max_e, 0, sizeof(max_e));
	memset((char *)bufPos, 0, sizeof(bufPos));
	memset((char *)Feature, 0, sizeof(Feature));

	if (n < 48 * 16) {
		goto calF;
	}
	
	for(sub_region = 0;sub_region < 48;sub_region++) {
		int start_pos = sub_region * 16;
		memset((char *)max_e, 0, sizeof(max_e));
		for(i = start_pos;i < start_pos + 16; i++) {
			int num = p[i];
			for(mapping = 0;mapping < 12;mapping++) {
				max_e[mapping] = max((unsigned char)num, max_e[mapping]);
				num = (num + step) & 0xff;
			}
		}
		for(mapping = 0;mapping < 12;mapping++) {
			SLIDE(max_e[mapping], fingerprint[mapping], bufPos[mapping], buf[mapping]);
		}
	}		
	for(mapping = 0;mapping < 12;mapping++) {
		Feature[mapping] = fingerprint[mapping];
	}
	while(i < n) {
		int start_pos = sub_region * 16;
		int end_pos = min(start_pos + 16, n);
		memset((char *)max_e, 0, sizeof(max_e));
		for(i = start_pos;i < end_pos; i++) {
			int num = p[i];
			for(mapping = 0;mapping < 12;mapping++) {
				max_e[mapping] = max((unsigned char)num, max_e[mapping]);
				num = (num + step) & 0xff;
			}
		}
		for(mapping = 0;mapping < 12;mapping++) {
			SLIDE(max_e[mapping], fingerprint[mapping], bufPos[mapping], buf[mapping]);
			Feature[mapping] = max(Feature[mapping], fingerprint[mapping]);
		}
		sub_region++;
	}

calF:
	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature);
	TempFeatures[1] = *(Feature + 3);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 9);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 1);
	TempFeatures[1] = *(Feature + 4);
	TempFeatures[2] = *(Feature + 7);
	TempFeatures[3] = *(Feature + 10);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 2);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 8);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n", __FILE__, __LINE__, SF[0]);
	return;
}

void sisd_rabin_fixed(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	i = 48;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (j = 48; j >= 2; j--)
		SLIDE(p[i - j], fingerprint, bufPos, buf);

	for (subregion_num = 0, jj = 0; subregion_num < destor.super_feature_subregion_num;
		 subregion_num++, jj++)
	{
		endpos += subregion_size;
		if (endpos > n)
			endpos = n;

		while (i <= endpos)
		{
			SLIDE(p[i - 1], fingerprint, bufPos, buf);
			// fingerprint = (fingerprint << 1) + (g_gear_matrix[p[i]]);

			if (Feature[jj] < fingerprint)
				Feature[jj] = fingerprint;

			i++;
		}
	}

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature);
	TempFeatures[1] = *(Feature + 3);
	TempFeatures[2] = *(Feature + 6);
	TempFeatures[3] = *(Feature + 9);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 1);
	TempFeatures[1] = *(Feature + 4);
	TempFeatures[2] = *(Feature + 7);
	TempFeatures[3] = *(Feature + 10);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 2);
	TempFeatures[1] = *(Feature + 5);
	TempFeatures[2] = *(Feature + 8);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n", __FILE__, __LINE__, SF[0]);
	return;
}

void sisd_gear_fixed_avx(unsigned char *p, int n, struct superF *SF)
{
	destor.super_feature_subregion_num = 16;
	int subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	int addition = 64;
	if ((subregion_size + addition) % 4 != 0)
	{
		addition = 4 - (subregion_size + addition) % 4 + addition;
		assert((addition + subregion_size) % 4 == 0);
	}

	__m512i vindex = _mm512_setr_epi32(
		0, 1 * subregion_size - addition, 2 * subregion_size - addition, 3 * subregion_size - addition,
		4 * subregion_size - addition, 5 * subregion_size - addition, 6 * subregion_size - addition, 7 * subregion_size - addition,
		8 * subregion_size - addition, 9 * subregion_size - addition, 10 * subregion_size - addition, 11 * subregion_size - addition,
		12 * subregion_size - addition, 13 * subregion_size - addition, 14 * subregion_size - addition, 15 * subregion_size - addition);

	__m512i cmask = _mm512_set1_epi32((long)0xff);
	__m512i submask = _mm512_set_epi64(0, 0, 0, 0, 0, 0, 0, 0xffffffffffffffff);
	__m512i vfeature1 = _mm512_set1_epi64(0LL);
	__m512i vfeature2 = _mm512_set1_epi64(0LL);

	i = 0;
	__m512i hash1 = _mm512_set1_epi64(0LL);
	__m512i hash2 = _mm512_set1_epi64(0LL);
	__m512i kickBytes = _mm512_set1_epi32(0LL);
	int64_t tmphash = 0;
	while (i < subregion_size + addition)
	{
		__m512i cbytes = _mm512_i32gather_epi32(vindex, (void *)(((uint8_t *)p) + i), 1);

		for (j = 0; j < 4; j++)
		{
			__m256i m1 = _mm512_extracti32x8_epi32(_mm512_and_epi32(cbytes, cmask), 0);
			__m256i m2 = _mm512_extracti32x8_epi32(_mm512_and_epi32(cbytes, cmask), 1);

			hash1 = _mm512_slli_epi64(hash1, 1);
			__m512i gear_val = _mm512_i32gather_epi64(m1, g_gear_matrix, 8);
			hash1 = _mm512_add_epi64(hash1, gear_val);

			hash2 = _mm512_slli_epi64(hash2, 1);
			gear_val = _mm512_i32gather_epi64(m2, g_gear_matrix, 8);
			hash2 = _mm512_add_epi64(hash2, gear_val);

			cbytes = _mm512_srli_epi64(cbytes, 8);
			if (i + j >= 63 && i + j < addition)
			{
				vfeature1 = _mm512_max_epi64(vfeature1, hash1);
				if (i + j == addition - 1)
				{
					vfeature1 = _mm512_and_epi64(vfeature1, submask);
				}
			}
			else if (i + j >= addition)
			{
				vfeature1 = _mm512_max_epi64(vfeature1, hash1);
				vfeature2 = _mm512_max_epi64(vfeature2, hash2);
				if (i + j == subregion_size - 1)
				{
					__m512i vfeature3 = _mm512_and_epi64(vfeature1, submask);
					tmphash = _mm512_reduce_or_epi64(vfeature3);
				}
			}
		}

		i += 4;
	}

	__m512i vindex2 = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7);
	_mm512_i64scatter_epi64((void *)Feature, vindex2, vfeature1, 8);
	vindex2 = _mm512_setr_epi64(8, 9, 10, 11, 12, 13, 14, 15);
	_mm512_i64scatter_epi64((void *)Feature, vindex2, vfeature2, 8);

	Feature[0] = tmphash;

	Feature[0] = max(Feature[0], Feature[1]);
	Feature[1] = max(Feature[2], Feature[3]);
	Feature[2] = max(Feature[4], Feature[5]);
	Feature[3] = max(Feature[6], Feature[7]);
	Feature[4] = max(Feature[8], Feature[9]);
	Feature[5] = max(Feature[10], Feature[11]);
	Feature[6] = max(Feature[12], Feature[13]);
	Feature[7] = max(Feature[14], Feature[15]);
	Feature[8] = max(Feature[0], Feature[1]);
	Feature[9] = max(Feature[2], Feature[3]);
	Feature[10] = max(Feature[4], Feature[5]);
	Feature[11] = max(Feature[6], Feature[7]);

	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 0);
	TempFeatures[1] = *(Feature + 2);
	TempFeatures[2] = *(Feature + 4);
	TempFeatures[3] = *(Feature + 6);
	uint64_t temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf1 = temp_hash;

	TempFeatures[0] = *(Feature + 1);
	TempFeatures[1] = *(Feature + 3);
	TempFeatures[2] = *(Feature + 5);
	TempFeatures[3] = *(Feature + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf2 = temp_hash;

	feature_size = 4 * sizeof(UINT64);
	TempFeatures[0] = *(Feature + 8);
	TempFeatures[1] = *(Feature + 9);
	TempFeatures[2] = *(Feature + 10);
	TempFeatures[3] = *(Feature + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	SF->sf3 = temp_hash;
	// printf("%lld\n%lld\n%lld\n", SF->sf1, SF->sf2, SF->sf3);
	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

void sisd_gear_fixed(unsigned char *p, int n, struct superF *SF)
{
	int subregion_num, subregion_size = n / destor.super_feature_subregion_num;
	UINT64 Feature[24];
	UINT64 fingerprint;
	int i, j, bufPos, jj, ii, endpos;

	unsigned char om, Fbuf[256], *Tempbuf;
	unsigned char buf[128];
	memset((char *)buf, 0, 128);
	memset((char *)Feature, 0, sizeof(Feature));

	i = 48;
	bufPos = -1;
	fingerprint = 0;
	endpos = 0;

	for (j = 48; j >= 2; j--)
		SLIDE(p[i - j], fingerprint, bufPos, buf);

	for (subregion_num = 0, jj = 0; subregion_num < destor.super_feature_subregion_num;
		 subregion_num++, jj++)
	{
		endpos += subregion_size;
		if (endpos > n)
			endpos = n;

		while (i <= endpos)
		{
			fingerprint = (fingerprint << 1) + g_gear_matrix[p[i - 1]]; // 64 bit, more accurate
			// fingerprint = (fingerprint << 1) + GEAR[p[i - 1]];
			if (fingerprint > Feature[jj])
				Feature[jj] = fingerprint;
			i++;
		}
	}

	/* grouping phase */
	if (destor.features_per_sf == 3)
		get_sketch_threefeature_per_sf(SF, Feature);
	else if (destor.features_per_sf == 4)
	{
		if (destor.enable_sort_features)
			get_sketch_fourfeature_per_sf_sorted(SF, Feature);
		else
			get_sketch_fourfeature_per_sf(SF, Feature);
	}
	else
	{
		get_sketch_fivefeature_per_sf(SF, Feature);
	}

	// printf("%s,%d:Supfeature[0]: %lld.\n",__FILE__,__LINE__, SF[0]);
	return;
}

void featureSorting(UINT64 *Features)
{
	int i, j;
	for (i = 0; i < 11; i++)
	{
		int isSorted = 1;
		for (j = 0; j < 11 - i; j++)
		{
			if (Features[j] > Features[j + 1])
			{
				isSorted = 0;
				UINT64 temp = Features[j];
				Features[j] = Features[j + 1];
				Features[j + 1] = temp;
			}
		}
		if (isSorted)
			break;
	}
}

void featureSortingSP(UINT64 *Features)
{
	feature_bubble_sort(Features, 3);
	feature_bubble_sort(Features + 3, 3);
	feature_bubble_sort(Features + 6, 3);
	feature_bubble_sort(Features + 9, 3);
}
void feature_bubble_sort(UINT64 *a, int len)
{
	int max = len - 1;
	UINT64 tmp;
	int i, j;
	for (i = 0; i < max; i++)
	{
		for (j = 0; j < max - i; j++)
		{
			if (a[j + 1] > a[j])
			{
				tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
}

UINT64 featureSelecting(UINT64 *features, int n, int featureType)
{
	int i;
	UINT64 tempFeature = *(features);
	if (featureType == 1)
	{
		/* return the max feature */
		for (i = 1; i < n; i++)
		{
			if (tempFeature < *(features + i))
				tempFeature = *(features + i);
		}
	}
	else if (featureType == 2)
	{
		/* return the min feature */
		for (i = 1; i < n; i++)
		{
			if (tempFeature > *(features + i))
				tempFeature = *(features + i);
		}
	}
	return tempFeature;
}

inline void get_sketch_threefeature_four_sf(struct superF *sketches, UINT64 *features)
{

	uint64_t temp_hash;
	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);

	featureSortingSP(features);
	TempFeatures[0] = *(features);
	TempFeatures[1] = *(features + 3);
	TempFeatures[2] = *(features + 6);
	TempFeatures[3] = *(features + 9);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf1 = temp_hash;

	TempFeatures[0] = *(features + 1);
	TempFeatures[1] = *(features + 4);
	TempFeatures[2] = *(features + 7);
	TempFeatures[3] = *(features + 10);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf2 = temp_hash;

	TempFeatures[0] = *(features + 2);
	TempFeatures[1] = *(features + 5);
	TempFeatures[2] = *(features + 8);
	TempFeatures[3] = *(features + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf3 = temp_hash;
}

inline void get_sketch_threefeature_per_sf(struct superF *sketches, UINT64 *features)
{

	uint64_t temp_hash;
	UINT64 TempFeatures[3];
	int feature_size = 3 * sizeof(UINT64);

	TempFeatures[0] = featureSelecting(features, 4, 1);
	TempFeatures[1] = featureSelecting(features + 4, 4, 1);
	TempFeatures[2] = featureSelecting(features + 8, 4, 1);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf1 = temp_hash;

	TempFeatures[0] = featureSelecting(features, 4, 2);
	TempFeatures[1] = featureSelecting(features + 4, 4, 2);
	TempFeatures[2] = featureSelecting(features + 8, 4, 2);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf2 = temp_hash;

	featureSorting(features);
	TempFeatures[0] = *(features);
	TempFeatures[1] = *(features + 1);
	TempFeatures[2] = *(features + 2);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf3 = temp_hash;
}

inline void get_sketch_fourfeature_per_sf_sorted(struct superF *sketches, UINT64 *features)
{
	uint64_t temp_hash;
	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);
	featureSorting(features);
	TempFeatures[0] = *(features);
	TempFeatures[1] = *(features + 1);
	TempFeatures[2] = *(features + 2);
	TempFeatures[3] = *(features + 3);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf1 = temp_hash;

	TempFeatures[0] = *(features + 4);
	TempFeatures[1] = *(features + 5);
	TempFeatures[2] = *(features + 6);
	TempFeatures[3] = *(features + 7);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf2 = temp_hash;

	TempFeatures[0] = *(features + 8);
	TempFeatures[1] = *(features + 9);
	TempFeatures[2] = *(features + 10);
	TempFeatures[3] = *(features + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf3 = temp_hash;
}

inline void get_sketch_fourfeature_per_sf(struct superF *sketches, UINT64 *features)
{
	uint64_t temp_hash;
	UINT64 TempFeatures[4];
	int feature_size = 4 * sizeof(UINT64);

	// featureSorting(features);
	TempFeatures[0] = *(features);
	TempFeatures[1] = *(features + 3);
	TempFeatures[2] = *(features + 6);
	TempFeatures[3] = *(features + 9);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf1 = temp_hash;

	// featureSorting(features);
	TempFeatures[0] = *(features + 1);
	TempFeatures[1] = *(features + 4);
	TempFeatures[2] = *(features + 7);
	TempFeatures[3] = *(features + 10);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf2 = temp_hash;

	// featureSorting(features);
	TempFeatures[0] = *(features + 2);
	TempFeatures[1] = *(features + 5);
	TempFeatures[2] = *(features + 8);
	TempFeatures[3] = *(features + 11);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf3 = temp_hash;
}

inline void get_sketch_fivefeature_per_sf(struct superF *sketches, UINT64 *features)
{
	uint64_t temp_hash;
	UINT64 TempFeatures[5];
	int feature_size = 5 * sizeof(UINT64);

	TempFeatures[0] = *(features);
	TempFeatures[1] = *(features + 3);
	TempFeatures[2] = *(features + 6);
	TempFeatures[3] = *(features + 9);
	TempFeatures[4] = *(features + 12);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf1 = temp_hash;

	TempFeatures[0] = *(features + 1);
	TempFeatures[1] = *(features + 4);
	TempFeatures[2] = *(features + 7);
	TempFeatures[3] = *(features + 10);
	TempFeatures[4] = *(features + 13);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf2 = temp_hash;

	TempFeatures[0] = *(features + 2);
	TempFeatures[1] = *(features + 5);
	TempFeatures[2] = *(features + 8);
	TempFeatures[3] = *(features + 11);
	TempFeatures[4] = *(features + 14);
	temp_hash = spooky_hash64((unsigned char *)TempFeatures, feature_size, 12345678);
	sketches->sf3 = temp_hash;
}
